
// 17.23 in 802.1w-2001

#include "802_1w_2001_procedures.h"
#include "bridge.h"
#include "port.h"
#include "log.h"
#include "assert.h"

enum
{
	UNDEFINED,
	
	INIT_PORT,
	BLOCK_PORT,
	BLOCKED_PORT,
	BACKUP_PORT,
	
	ROOT_PORT,
	ROOT_PROPOSED,
	ROOT_AGREED,
	REROOT,
	ROOT_FORWARD,
	ROOT_LEARN,
	REROOTED,
	
	DESIGNATED_PORT,
	DESIGNATED_FORWARD,
	DESIGNATED_PROPOSE,
	DESIGNATED_LEARN,
	DESIGNATED_SYNCED,
	DESIGNATED_LISTEN,
	DESIGNATED_RETIRED,
};

// ============================================================================

const char* PortRoleTransitions_802_1w_2001_GetStateName (SM_STATE state)
{
	switch (state)
	{
		case INIT_PORT:			return "INIT_PORT";
		case BLOCK_PORT:		return "BLOCK_PORT";
		case BLOCKED_PORT:		return "BLOCKED_PORT";
		case BACKUP_PORT:		return "BACKUP_PORT";
			
		case ROOT_PORT:			return "ROOT_PORT";
		case ROOT_PROPOSED:		return "ROOT_PROPOSED";
		case ROOT_AGREED:		return "ROOT_AGREED";
		case REROOT:			return "REROOT";
		case ROOT_FORWARD:		return "ROOT_FORWARD";
		case ROOT_LEARN:		return "ROOT_LEARN";
		case REROOTED:			return "REROOTED";
			
		case DESIGNATED_PORT:	return "DESIGNATED_PORT";
		case DESIGNATED_FORWARD:return "DESIGNATED_FORWARD";
		case DESIGNATED_PROPOSE:return "DESIGNATED_PROPOSE";
		case DESIGNATED_LEARN:	return "DESIGNATED_LEARN";
		case DESIGNATED_SYNCED:	return "DESIGNATED_SYNCED";
		case DESIGNATED_LISTEN:	return "DESIGNATED_LISTEN";
		case DESIGNATED_RETIRED:return "DESIGNATED_RETIRED";

		default:				return "(undefined)";
	}
}

// ============================================================================

SM_STATE PortRoleTransitions_802_1w_2001_CheckConditions (BRIDGE* bridge, int givenPort, int givenTree, SM_STATE state)
{
	assert (givenPort != -1);
	assert (givenTree != -1);
	
	PORT* port = bridge->ports [givenPort];
	PORT_TREE* portTree = port->trees [givenTree];

	// ------------------------------------------------------------------------
	// Check global conditions.
	
	if (bridge->BEGIN)
	{
		if (state == INIT_PORT)
		{
			// The entry block for this state has been executed already.
			return 0;
		}
		
		return INIT_PORT;
	}
	
	if (portTree->selected && !portTree->updtInfo)
	{
		if (((portTree->selectedRole == PORT_ROLE_DISABLED) || (portTree->selectedRole == PORT_ROLE_ALTERNATE) || (portTree->selectedRole == PORT_ROLE_BACKUP))
			&& (portTree->role != portTree ->selectedRole))
		{
			return BLOCK_PORT;
		}

		if ((portTree->selectedRole == PORT_ROLE_ROOT) && (portTree->role != portTree->selectedRole))
			return ROOT_PORT;

		if ((portTree->selectedRole == PORT_ROLE_DESIGNATED) && (portTree->role != portTree->selectedRole))
			return DESIGNATED_PORT;
	}

	// ------------------------------------------------------------------------
	// Check exit conditions from each state.
	
	// ------------------------------------------------------------------------
	// Disabled / Alternate / Backup
	
	if (state == INIT_PORT)
	{
		// This transition is not in the standard, but without it we might have both the role and the
		// selectedRole set to DesignatedPort (by the BLOCK_PORT code), but stuck in BLOCKED_PORT.
		if (portTree->selected && !portTree->updtInfo)
		{
			if (!portTree->learning && !portTree->forwarding)
				return BLOCKED_PORT;
		}
		
		return 0;
	}

	if (state == BLOCK_PORT)
	{
		if (portTree->selected && !portTree->updtInfo)
		{
			if (!portTree->learning && !portTree->forwarding)
				return BLOCKED_PORT;
		}
		
		return 0;
	}

	if (state == BLOCKED_PORT)
	{
		if (portTree->selected && !portTree->updtInfo)
		{
			if ((portTree->fdWhile != port->FwdDelay ()) || portTree->sync || portTree->reRoot || !portTree->synced)
				return BLOCKED_PORT;

			if ((portTree->rbWhile != 2 * bridge->trees [CIST_INDEX]->BridgeTimes.HelloTime) && (portTree->role == PORT_ROLE_BACKUP))
				return BACKUP_PORT;
		}
		
		return 0;
	}

	if (state == BACKUP_PORT)
		return BLOCKED_PORT;

	// ------------------------------------------------------------------------
	// Root
	
	if (state == ROOT_PORT)
	{
		if (portTree->selected && !portTree->updtInfo)
		{
			if (portTree->proposed && !portTree->synced)
				return ROOT_PROPOSED;
			
			if ((portTree->proposed && allSynced_802_1w_2001 (bridge, givenPort)) || (!portTree->synced && allSynced_802_1w_2001 (bridge, givenPort)))
				return ROOT_AGREED;
			
			if (!portTree->forward && !portTree->reRoot)
				return REROOT;
			
			if (portTree->rrWhile != port->FwdDelay ())
				return ROOT_PORT;
			
			if (portTree->reRoot && portTree->forward)
				return REROOTED;
			
			if (((portTree->fdWhile == 0) || (reRooted_802_1w_2001 (bridge, givenPort) && (portTree->rbWhile == 0)) && (bridge->ForceProtocolVersion >= 2)) && !portTree->learn)
				return ROOT_LEARN;
			
			if (((portTree->fdWhile == 0) || (reRooted_802_1w_2001 (bridge, givenPort) && (portTree->rbWhile == 0)) && (bridge->ForceProtocolVersion >= 2)) && portTree->learn && !portTree->forward)
				return ROOT_FORWARD;
		}
		
		return 0;
	}
	
	if (state == ROOT_PROPOSED)
		return ROOT_PORT;
	
	if (state == ROOT_AGREED)
		return ROOT_PORT;
	
	if (state == REROOT)
		return ROOT_PORT;
	
	if (state == ROOT_FORWARD)
		return ROOT_PORT;
	
	if (state == ROOT_LEARN)
		return ROOT_PORT;
	
	if (state == REROOTED)
		return ROOT_PORT;
	
	// ------------------------------------------------------------------------
	// Designated

	if (state == DESIGNATED_PORT)
	{
		if (portTree->selected && !portTree->updtInfo)
		{
			if (!portTree->forward && !portTree->agreed && !portTree->proposing && !port->operEdge)
				return DESIGNATED_PROPOSE;
			
			if ((!portTree->learning && !portTree->forwarding && !portTree->synced) || (portTree->agreed && !portTree->synced) || (port->operEdge && !portTree->synced) || (portTree->sync && portTree->synced))
				return DESIGNATED_SYNCED;
			
			if ((portTree->rrWhile == 0) && portTree->reRoot)
				return DESIGNATED_RETIRED;
			
			if (((portTree->sync && !portTree->synced) || (portTree->reRoot && (portTree->rrWhile != 0))) && !port->operEdge && (portTree->learn || portTree->forward))
				return DESIGNATED_LISTEN;
				
			if (((portTree->fdWhile == 0) || portTree->agreed || port->operEdge) && ((portTree->rrWhile ==0) || !portTree->reRoot) && !portTree->sync && !portTree->learn)
				return DESIGNATED_LEARN;
			
			if (((portTree->fdWhile == 0) || portTree->agreed || port->operEdge) && ((portTree->rrWhile ==0) || !portTree->reRoot) && !portTree->sync && (portTree->learn && !portTree->forward))
				return DESIGNATED_FORWARD;			
		}
		
		return 0;
	}
	
	if (state == DESIGNATED_FORWARD)
		return DESIGNATED_PORT;

	if (state == DESIGNATED_PROPOSE)
		return DESIGNATED_PORT;
	
	if (state == DESIGNATED_LEARN)
		return DESIGNATED_PORT;
	
	if (state == DESIGNATED_SYNCED)
		return DESIGNATED_PORT;
	
	if (state == DESIGNATED_LISTEN)
		return DESIGNATED_PORT;
	
	if (state == DESIGNATED_RETIRED)
		return DESIGNATED_PORT;
		
	assert (false);
	return 0;
}

// ============================================================================

void PortRoleTransitions_802_1w_2001_InitState (BRIDGE* bridge, int givenPort, int givenTree, SM_STATE state)
{
	assert (givenPort != -1);
	assert (givenTree != -1);
	
	PORT* port = bridge->ports [givenPort];
	PORT_TREE* portTree = port->trees [givenTree];

	// ------------------------------------------------------------------------
	// Disabled / Alternate / Backup
	
	if (state == INIT_PORT)
	{
		portTree->role = PORT_ROLE_DISABLED;
		portTree->synced = 0;
		portTree->sync = portTree->reRoot = 1;
		portTree->rrWhile = portTree->fdWhile = port->FwdDelay ();
		portTree->rbWhile = 0;
	}
	else if (state == BLOCK_PORT)
	{
		portTree->role = portTree->selectedRole;
		portTree->learn = portTree->forward = 0;
	}		
	else if (state == BLOCKED_PORT)
	{
		portTree->fdWhile = port->FwdDelay ();
		portTree->synced = 1; portTree->rrWhile = 0;
		portTree->sync = portTree->reRoot = 0;
	}		
	else if (state == BACKUP_PORT)
	{
		portTree->rbWhile = 2 * bridge->trees [CIST_INDEX]->BridgeTimes.HelloTime;
	}		
	
	// ------------------------------------------------------------------------
	// Root
	
	else if (state == ROOT_PORT)
	{
		portTree->role = PORT_ROLE_ROOT;
		portTree->rrWhile = port->FwdDelay ();
	}		
	else if (state == ROOT_PROPOSED)
	{
		setSyncBridge_802_1w_2001 (bridge);
		portTree->proposed = false;
	}
	else if (state == ROOT_AGREED)
	{
		portTree->proposed = portTree->sync = 0;
		portTree->synced = 1;
		port->newInfo = 1;
	}		
	else if (state == REROOT)
	{
		setReRootBridge_802_1w_2001 (bridge);
	}		
	else if (state == ROOT_FORWARD)
	{
		portTree->fdWhile = 0;
		portTree->forward = true;
	}		
	else if (state == ROOT_LEARN)
	{
		portTree->fdWhile = port->forwardDelay ();
		portTree->learn = true;
	}		
	else if (state == REROOTED)
	{
		portTree->reRoot = false;
	}
		
	// ------------------------------------------------------------------------
	// Designated
	
	else if (state == DESIGNATED_PORT)
	{
		portTree->role = PORT_ROLE_DESIGNATED;
	}		
	else if (state == DESIGNATED_FORWARD)
	{
		portTree->forward = true;
		portTree->fdWhile = 0;
	}		
	else if (state == DESIGNATED_PROPOSE)
	{
		portTree->proposing = 1;
		port->newInfo = 1;			
	}		
	else if (state == DESIGNATED_LEARN)
	{
		portTree->learn = 1;
		portTree->fdWhile= port->forwardDelay ();
	}		
	else if (state == DESIGNATED_SYNCED)
	{
		portTree->rrWhile = 0;
		portTree->synced = 1;
		portTree->sync = 0;
	}		
	else if (state == DESIGNATED_RETIRED)
	{
		portTree->reRoot = 0;
	}
	else if (state == DESIGNATED_LISTEN)
	{
		portTree->learn = portTree->forward = 0;
		portTree->fdWhile= port->FwdDelay ();
	}
	else
		assert (false);
}
